package pers.jsan.litheopen.security.config;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import pers.jsan.litheopen.security.cache.CacheClientManager;
import pers.jsan.litheopen.security.cache.RedisClientManager;
import pers.jsan.litheopen.security.cache.RedisUserManager;
import pers.jsan.litheopen.security.filter.authorize.AuthorizeParamsFilter;
import pers.jsan.litheopen.security.filter.authorize.CacheSessionFilter;
import pers.jsan.litheopen.security.filter.authorize.CheckClientFilter;
import pers.jsan.litheopen.security.filter.client.CheckClientAccessFilter;
import pers.jsan.litheopen.security.filter.client.ClientAccessRequestTimesFilter;
import pers.jsan.litheopen.security.filter.login.CheckLoginNameAndPasswordFilter;
import pers.jsan.litheopen.security.filter.login.GetSessionFilter;
import pers.jsan.litheopen.security.interceptor.*;
import pers.jsan.litheopen.security.oauth.service.IUserDetailService;
import pers.jsan.litheopen.security.prover.AuthenticationManager;
import pers.jsan.litheopen.security.prover.LitheAuthenticationProver;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class LitheAuthenticationConfig implements WebMvcConfigurer {

    @Autowired
    private CheckClientFilter checkClientFilter;

    @Autowired
    private CheckClientAccessFilter checkClientAccessFilter;

    @Autowired
    private ClientAccessRequestTimesFilter clientAccessRequestTimesFilter;

    public AuthenticationManager initConfigure() {
        return new AuthenticationManager();
    }

    @Bean
    public AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = initConfigure();
        authenticationManager
                //登录认证
                .addAuthenticationFilter(new CheckLoginNameAndPasswordFilter())
                .addAuthenticationFilter(new GetSessionFilter())
                //待授权
                .addAuthorizeFilter(new CacheSessionFilter())
                .addAuthorizeFilter(new AuthorizeParamsFilter())
                .addAuthorizeFilter(checkClientFilter)
                //客户端
                .addClientFilter(clientAccessRequestTimesFilter)
                .addClientFilter(checkClientAccessFilter)
                //临时授权码获取token
                .addUserGetTokenFilter(checkClientAccessFilter)
                //停用客户端校验拦截器
                .setClientOnlineDisable(true);
        return authenticationManager;
    }

    @Bean
    public LitheAuthenticationInterceptor litheAuthenticationInterceptor() {
        return new LitheAuthenticationInterceptor().setAuthenticationManager(authenticationManager());
    }

    @Bean
    public LitheAuthorizeInterceptor litheAuthorizeInterceptor() {
        return new LitheAuthorizeInterceptor().setAuthenticationManager(authenticationManager());
    }

    @Bean
    public LitheAccessTokenInterceptor litheAccessTokenInterceptor() {
        return new LitheAccessTokenInterceptor().setAuthenticationManager(authenticationManager());
    }

    @Bean
    public LitheUserGetTokenInterceptor litheUserGetTokenInterceptor() {
        return new LitheUserGetTokenInterceptor().setAuthenticationManager(authenticationManager());
    }

    @Bean
    public UserOnlineInterceptor userOnlineInterceptor() {
        return new UserOnlineInterceptor().setAuthenticationManager(authenticationManager());
    }

    @Bean
    public ClientOnlineInterceptor clientOnlineInterceptor() {
        return new ClientOnlineInterceptor().setAuthenticationManager(authenticationManager())
                .setRedisClientManager(redisClientManager());
    }

    public void addInterceptors(InterceptorRegistry registry) {
        //认证拦截器
        LitheAuthenticationInterceptor litheAuthenticationInterceptor = litheAuthenticationInterceptor();
        registry.addInterceptor(litheAuthenticationInterceptor)
                .addPathPatterns(litheAuthenticationInterceptor.getAuthenticationManager().getLoginPath());
        //授权校验拦截器
        LitheAuthorizeInterceptor litheAuthorizeInterceptor = litheAuthorizeInterceptor();
        registry.addInterceptor(litheAuthorizeInterceptor)
                .addPathPatterns(litheAuthorizeInterceptor.getAuthenticationManager().getAuthorizePath());
        //获取accessToken拦截器
        LitheAccessTokenInterceptor litheAccessTokenInterceptor = litheAccessTokenInterceptor();
        registry.addInterceptor(litheAccessTokenInterceptor)
                .addPathPatterns(litheAccessTokenInterceptor.getAuthenticationManager().getAccessTokenPath());
        //临时授权码获取用户凭证拦截器
        LitheUserGetTokenInterceptor litheUserGetTokenInterceptor = litheUserGetTokenInterceptor();
        registry.addInterceptor(litheUserGetTokenInterceptor)
                .addPathPatterns(litheUserGetTokenInterceptor.getAuthenticationManager().getUserGetTokenPath());
        //用户凭证在线拦截器，如果设置停止用户校验，无效。
        UserOnlineInterceptor userOnlineInterceptor = userOnlineInterceptor();
        if(!userOnlineInterceptor.getAuthenticationManager().isUserOnlineDisable()) {
            String[] userInterceptPaths = userOnlineInterceptor.getAuthenticationManager().getUserInterceptPath().split(",");
            String userExcludePath = userOnlineInterceptor.getAuthenticationManager().getUserExcludePath();
            List<String> userExcludePaths = new ArrayList<>();
            userExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getLoginPath());
            userExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getLoginPage());
            userExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getAuthorizePath());
            userExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getAccessTokenPath());
            userExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getUserGetTokenPath());
            if (StringUtils.isNotEmpty(userExcludePath)) {
                String[] userExcludePathArr = userExcludePath.split(",");
                userExcludePaths.addAll(Arrays.asList(userExcludePathArr));
            }
            registry.addInterceptor(userOnlineInterceptor)
                    .addPathPatterns(userInterceptPaths)
                    .excludePathPatterns(userExcludePaths);
        }
        //客户端凭证在线拦截器，如果设置停止客户端校验，无效。
        ClientOnlineInterceptor clientOnlineInterceptor = clientOnlineInterceptor();
        if(!clientOnlineInterceptor.getAuthenticationManager().isClientOnlineDisable()) {
            //客户端凭证在线拦截的，需认证接口
            String clientInterceptPath = userOnlineInterceptor.getAuthenticationManager().getUserExcludePath();
            List<String> clientInterceptPaths = new ArrayList<>();
            if (StringUtils.isNotEmpty(clientInterceptPath)) {
                String[] clientInterceptPathArr = clientInterceptPath.split(",");
                clientInterceptPaths.addAll(Arrays.asList(clientInterceptPathArr));
            }
            //客户端凭证在线拦截器，免认证接口
            String clientExcludePath = userOnlineInterceptor.getAuthenticationManager().getUserExcludePath();
            List<String> clientExcludePaths = new ArrayList<>();
            clientExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getLoginPath());
            clientExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getLoginPage());
            clientExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getAuthorizePath());
            clientExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getAccessTokenPath());
            clientExcludePaths.add(userOnlineInterceptor.getAuthenticationManager().getUserGetTokenPath());
            if (StringUtils.isNotEmpty(clientExcludePath)) {
                String[] clientExcludePathArr = clientExcludePath.split(",");
                clientExcludePaths.addAll(Arrays.asList(clientExcludePathArr));
            }
            registry.addInterceptor(clientOnlineInterceptor)
                    .addPathPatterns(clientInterceptPaths)
                    .excludePathPatterns(clientExcludePaths);
        }
    }

    @Bean("redisUserTemplate")
    public RedisTemplate redisUserTemplate(RedisConnectionFactory factory) {
        return getRedisTemplate(factory);
    }

    @Bean("redisClientTemplate")
    public RedisTemplate redisClientTemplate(RedisConnectionFactory factory) {
        return getRedisTemplate(factory);
    }

    private RedisTemplate getRedisTemplate(RedisConnectionFactory factory) {
        RedisSerializer stringSerializer = new StringRedisSerializer();
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        return redisTemplate;
    }

    @Bean
    public CacheClientManager redisClientManager() {
        return new RedisClientManager();
    }
}
